package com.qyer.commons.utils;

import com.google.common.collect.Maps;
import com.qyer.commons.exception.InvocationException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * User: Z J Wu Date: 2016/01/05 Time: 17:10 Package: com.qyer.search.utils
 */
public class InvocationUtils {

  public static void genericRuntimeException() {
    throw new RuntimeException("This is a test runtime exception.");
  }

  public static <F> F getFutureContent(Future<F> future, TimeUnit timeUnit, long timeOut) throws
    InvocationException {
    try {
      return future.get(timeOut, timeUnit);
    } catch (ExecutionException e) {
      throw new InvocationException(e.getCause());
    } catch (Exception e) {
      throw new InvocationException(e);
    }
  }

  public static <F> List<F> getFutureContents(List<Future<F>> futureList, TimeUnit timeUnit,
                                              long timeOut) throws InvocationException {
    if (CollectionUtils.isEmpty(futureList)) {
      return Collections.emptyList();
    }
    List<F> list = new ArrayList<>(futureList.size());
    long t1, t2, timeUse = 0;
    F f;
    try {
      for (Future<F> future : futureList) {
        t1 = System.currentTimeMillis();
        f = future.get(timeOut - timeUse, timeUnit);
        list.add(f);
        t2 = System.currentTimeMillis();
        timeUse += (t2 - t1);
      }
    } catch (ExecutionException e) {
      throw new InvocationException(e.getCause());
    } catch (Exception e) {
      throw new InvocationException(e);
    }
    return list;
  }

  public static <K, V> Map<K, V> getFutureContentMap(Map<K, Future<V>> futureMap, TimeUnit timeUnit,
                                                     long timeOut) throws InvocationException {
    if (MapUtils.isEmpty(futureMap)) {
      return null;
    }
    Map<K, V> contentMap = Maps.newHashMapWithExpectedSize(futureMap.size());
    long t1, t2, timeUse = 0;
    K key;
    V value;
    try {
      for (Map.Entry<K, Future<V>> entry : futureMap.entrySet()) {
        t1 = System.currentTimeMillis();
        key = entry.getKey();
        value = entry.getValue().get(timeOut - timeUse, timeUnit);
        contentMap.put(key, value);
        t2 = System.currentTimeMillis();
        timeUse += (t2 - t1);
      }
    } catch (ExecutionException e) {
      throw new InvocationException(e.getCause());
    } catch (Exception e) {
      throw new InvocationException(e);
    }

    return contentMap;
  }
}
